﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Data.SqlClient;
using System.Data;
using Cinchoo.Core;
using Cinchoo.Core.ETL;

namespace ChoWorkflowServer.Model
{
    public enum ChoRepeatIntervalType { Minutes = 0, Hours }

    public class ChoTaskTimeout
    {
        public static readonly int MinValue = 30;

        public static int Resolve(int timeout)
        {
            if (timeout > 0 && timeout < 30)
                timeout = 30;

            return timeout;
        }
    }

    public class ChoUser
    {
        public const string SYSTEM_ACCT = "SYSTEM";

        private static string _currentUser = Environment.UserName.ToUpper();
        public static string CurrentUser
        {
            get { return _currentUser; }
            set { _currentUser = value; }
        }
    }

    public class ChoCalendar
    {
        public const string NULL_CALENDAR_NAME = "<NULL>";
        public const int NULL_CALENDAR_ID = -1;

        public static int ResolveToId(int calId, string calName)
        {
            if (calName == NULL_CALENDAR_NAME)
                return NULL_CALENDAR_ID;
            else
                return calId;
        }
    }

    public class ChoDbDateTime
    {
        public static readonly DateTime MinValue = new DateTime(1753, 1, 1);
    }

    public class ChoTaskModel : INotifyPropertyChanged
    {
        private static readonly object _padLock = new object();
        private static ChoTaskGroupCategory RootTaskCategory = new ChoTaskGroupCategory(-1, "All Tasks", null);

        private ObservableCollection<ChoTaskGroupCategory> _categories = new ObservableCollection<ChoTaskGroupCategory>();
        private List<ChoTaskGroupCategory> _flatCategories = new List<ChoTaskGroupCategory>();

        public event PropertyChangedEventHandler PropertyChanged;
        internal static readonly ChoTasksDataSet.CHO_TASKSDataTable _tasksTable = new ChoTasksDataSet.CHO_TASKSDataTable();

        public ChoTaskModel()
        {
            RootTaskCategory.IsTaskGroup = true;
        }

        //public static ChoTaskGroupCategory Load(ChoTaskModel model, string taskName)
        //{
        //    lock (_padLock)
        //    {
        //        ChoTaskGroupCategory taskCategory = new ChoTaskGroupCategory(0, taskName, null);

        //        using (IChoDataModel dataModel = ChoDataModelFactory.New())
        //        {
        //            ChoTasksDataSet.CHO_TASKSDataTable table = GetData(taskCategory, false);

        //            foreach (ChoTasksDataSet.CHO_TASKSRow row in table)
        //            {
        //                string taskGroupName = row.TASK_GRP_NAME;
        //                if (taskGroupName.IsNullOrWhiteSpace()) return null;

        //                taskCategory._id = row.TASK_ID;
        //                taskCategory.SleepBetweenRetry = row.IsSLEEP_BETWEEN_RETRYNull() ? 0 : row.SLEEP_BETWEEN_RETRY;
        //                taskCategory.MaxNoOfRetry = row.IsNO_OF_RETRYNull() ? 0 : row.NO_OF_RETRY;
        //                taskCategory.Timeout = row.IsTIMEOUTNull() ? 0 : row.TIMEOUT;
        //                taskCategory.Description = row.IsTASK_DESCNull() ? String.Empty : row.TASK_DESC;
        //                taskCategory.MachineName = row.MACHINE_NAME;
        //                taskCategory.BatchMachineName = row.IsBATCH_MACHINE_NAMENull() ? null : row.BATCH_MACHINE_NAME;
        //                taskCategory.CreatedBy = row.IsTASK_CREATED_BYNull() ? String.Empty : row.TASK_CREATED_BY;
        //                taskCategory.CreatedDate = row.IsTASK_CREATED_DATENull() ? DateTime.MinValue : row.TASK_CREATED_DATE;
        //                taskCategory.ModifiedBy = row.IsTASK_MODIFIED_BYNull() ? String.Empty : row.TASK_MODIFIED_BY;
        //                taskCategory.ModifiedDate = row.IsTASK_MODIFIED_DATENull() ? DateTime.MinValue : row.TASK_MODIFIED_DATE;
        //                taskCategory.BatchModifiedBy = row.IsMODIFIED_BYNull() ? String.Empty : row.MODIFIED_BY;
        //                taskCategory.TaskStatus = row.IsTASK_STATUSNull() ? 0 : row.TASK_STATUS;
        //                taskCategory.CmdParameters = row.IsCMD_PARAMETERSNull() ? null : row.CMD_PARAMETERS;
        //                taskCategory.BatchCmdParameters = row.IsBATCH_CMD_PARAMETERSNull() ? null : row.BATCH_CMD_PARAMETERS;
        //                taskCategory.ExitCode = row.IsEXIT_CODENull() ? 0 : row.EXIT_CODE;
        //                taskCategory.BatchId = row.IsBATCH_IDNull() ? ChoRandom.NextRandom() : row.BATCH_ID;
        //                taskCategory.WorkflowStatus = row.IsTASK_STATUSNull() ? ChoETLWorkflowStatus.INACTIVE : (ChoETLWorkflowStatus)row.TASK_STATUS;
        //                taskCategory.ParentGroupId = row.IsPARENTNull() ? 0 : row.PARENT;
        //                taskCategory.IsDirty = false;

        //                ChoTaskGroupCategory cat = model.FlatCategories.FirstOrDefault((category) => category.Name == taskGroupName);
        //                if (cat == null)
        //                    throw new ApplicationException("Missing task group.");

        //                taskCategory.GroupName = cat.Name;
        //                taskCategory.Parent = cat;
        //            }
        //        }
        //        return taskCategory;
        //    }
        //}

        public static void Refresh(ChoTaskGroupCategory taskCategory, bool recursive = true)
        {
            if (taskCategory == null)
                return;

            using (IChoDataModel dataModel = ChoDataModelFactory.New())
            {
                ChoTasksDataSet.CHO_TASKSDataTable table = GetData(taskCategory, recursive);

                foreach (ChoTasksDataSet.CHO_TASKSRow row in table)
                {
                    if (taskCategory.IsTaskGroup)
                    {
                        string taskGroupName = row.TASK_GRP_NAME;
                        if (taskGroupName.IsNullOrWhiteSpace()) return;

                        taskCategory.Description = row.IsTASK_GRP_DESCNull() ? null : row.TASK_GRP_DESC as string;
                        taskCategory.CreatedBy = row.TASK_GRP_CREATED_BY;
                        taskCategory.CreatedDate = row.TASK_GRP_CREATED_DATE;
                        taskCategory.ModifiedBy = row.TASK_GRP_MODIFIED_BY;
                        taskCategory.ModifiedDate = row.TASK_GRP_MODIFIED_DATE;
                    }
                    else
                    {
                        string taskGroupName = row.TASK_GRP_NAME;
                        if (taskGroupName.IsNullOrWhiteSpace()) return;

                        string taskName = row.TASK_NAME;
                        if (taskName.IsNullOrWhiteSpace()) return;

                        taskCategory._id = row.TASK_ID;
                        taskCategory.WorkflowName = row.IsWORKFLOW_NAMENull() ? taskName : row.WORKFLOW_NAME;

                        if (!taskCategory.ScheduleName.IsNullOrWhiteSpace() && !row.IsSCHEDULE_NAMENull()
                            && row.SCHEDULE_NAME != taskCategory.ScheduleName)
                        {
                        }
                        else
                        {
                            taskCategory.SleepBetweenRetry = row.IsSLEEP_BETWEEN_RETRYNull() ? 0 : row.SLEEP_BETWEEN_RETRY;
                            taskCategory.MaxNoOfRetry = row.IsNO_OF_RETRYNull() ? 0 : row.NO_OF_RETRY;
                            taskCategory.Timeout = row.IsTIMEOUTNull() ? 0 : row.TIMEOUT;
                            taskCategory.Description = row.IsTASK_DESCNull() ? String.Empty : row.TASK_DESC;
                            taskCategory.MachineName = row.MACHINE_NAME;
                            taskCategory.BatchMachineName = row.IsBATCH_MACHINE_NAMENull() ? null : row.BATCH_MACHINE_NAME;
                            taskCategory.CreatedBy = row.IsTASK_CREATED_BYNull() ? String.Empty : row.TASK_CREATED_BY;
                            taskCategory.CreatedDate = row.IsTASK_CREATED_DATENull() ? DateTime.MinValue : row.TASK_CREATED_DATE;
                            taskCategory.ModifiedBy = row.IsTASK_MODIFIED_BYNull() ? String.Empty : row.TASK_MODIFIED_BY;
                            taskCategory.ModifiedDate = row.IsTASK_MODIFIED_DATENull() ? DateTime.MinValue : row.TASK_MODIFIED_DATE;
                            taskCategory.BatchModifiedBy = row.IsMODIFIED_BYNull() ? String.Empty : row.MODIFIED_BY;
                            taskCategory.TaskStatus = row.IsTASK_STATUSNull() ? 0 : row.TASK_STATUS;
                            taskCategory.CmdParameters = row.IsCMD_PARAMETERSNull() ? null : row.CMD_PARAMETERS;
                            taskCategory.BatchCmdParameters = row.IsBATCH_CMD_PARAMETERSNull() ? null : row.BATCH_CMD_PARAMETERS;
                            taskCategory.ExitCode = row.IsEXIT_CODENull() ? 0 : row.EXIT_CODE;
                            taskCategory.BatchId = row.IsBATCH_IDNull() ? ChoRandom.NextRandom() : row.BATCH_ID;
                            taskCategory.WorkflowStatus = row.IsTASK_STATUSNull() ? ChoETLWorkflowStatus.INACTIVE : (ChoETLWorkflowStatus)row.TASK_STATUS;
                            taskCategory.ParentGroupId = row.IsPARENTNull() ? 0 : row.PARENT;
                        }
                    }
                    taskCategory.IsDirty = false;
                }
            }
        }

        public void Refresh()
        {
            RootTaskCategory.Tasks.Clear();
            _categories.Clear();
            _flatCategories.Clear();
            _categories.Add(RootTaskCategory);
            _flatCategories.Add(RootTaskCategory);

            using (IChoDataModel dataModel = ChoDataModelFactory.New())
            {
                ChoTasksDataSet.CHO_TASKSDataTable table = GetData(null, true);

                ChoTaskGroupCategory taskGroupCategory = null;
                ChoTaskGroupCategory parentTaskGroupCategory = null;
                ChoTaskGroupCategory taskCategory = null;

                //Build root level task groups
                ChoTasksDataSet.CHO_TASKSRow row = null;
                foreach (string groupName in table.AsEnumerable().Select(row1 => row1.TASK_GRP_NAME).Distinct())
                {
                    row = (from x in table.AsEnumerable()
                           where x.TASK_GRP_NAME == groupName
                           select x).FirstOrDefault();
                    if (row == null) continue;

                    string taskGroupName = row.TASK_GRP_NAME;
                    if (taskGroupName.IsNullOrWhiteSpace()) continue;

                    parentTaskGroupCategory = row.IsPARENTNull() ? null : TryFindTaskGroupById(row.PARENT);
                    if (parentTaskGroupCategory == null)
                        parentTaskGroupCategory = RootTaskCategory;

                    taskGroupCategory = new ChoTaskGroupCategory(row.TASK_GRP_ID, taskGroupName, null, parentTaskGroupCategory);
                    taskGroupCategory.Description = row.IsTASK_GRP_DESCNull() ? null : row.TASK_GRP_DESC as string;
                    taskGroupCategory.CreatedBy = row.TASK_GRP_CREATED_BY;
                    taskGroupCategory.CreatedDate = row.TASK_GRP_CREATED_DATE;
                    taskGroupCategory.ModifiedBy = row.TASK_GRP_MODIFIED_BY;
                    taskGroupCategory.ModifiedDate = row.TASK_GRP_MODIFIED_DATE;
                    taskGroupCategory.GroupName = parentTaskGroupCategory.Name;
                    taskGroupCategory.ParentGroupId = row.IsPARENTNull() ? 0 : row.PARENT;
                    taskGroupCategory.IsDirty = false;
                    taskGroupCategory.IsTaskGroup = true;
                    parentTaskGroupCategory.Tasks.Add(taskGroupCategory);

                    _flatCategories.Add(taskGroupCategory);
                }

                var tasks = from x in table.AsEnumerable()
                            where !x.IsTASK_NAMENull()
                            select x.TASK_NAME;

                foreach (string taskName in tasks.Distinct())
                {
                    if (taskName.IsNullOrWhiteSpace())
                        continue;

                    ChoTasksDataSet.CHO_TASKSRow row1;
                    row1 = (from x in table.AsEnumerable()
                            where !x.IsTASK_NAMENull() && x.TASK_NAME == taskName
                            select x).FirstOrDefault();
                    if (row1 == null) continue;

                    parentTaskGroupCategory = row1.IsTASK_GRP_NAMENull() ? null : TryFindTaskGroupByName(row1.TASK_GRP_NAME);
                    if (parentTaskGroupCategory == null)
                        continue;

                    taskCategory = new ChoTaskGroupCategory(row1.TASK_ID, row1.TASK_NAME, row1.IsSCHEDULE_NAMENull() ? null : row1.SCHEDULE_NAME, parentTaskGroupCategory);
                    taskCategory.WorkflowName = row1.IsWORKFLOW_NAMENull() ? taskName : row1.WORKFLOW_NAME;
                    taskCategory.SleepBetweenRetry = row1.IsSLEEP_BETWEEN_RETRYNull() ? 0 : row1.SLEEP_BETWEEN_RETRY;
                    taskCategory.MaxNoOfRetry = row1.IsNO_OF_RETRYNull() ? 0 : row1.NO_OF_RETRY;
                    taskCategory.Timeout = row1.IsTIMEOUTNull() ? 0 : row1.TIMEOUT;
                    taskCategory.Description = row1.IsTASK_DESCNull() ? String.Empty : row1.TASK_DESC;
                    taskCategory.MachineName = row1.MACHINE_NAME;
                    taskCategory.BatchMachineName = row1.IsBATCH_MACHINE_NAMENull() ? null : row1.BATCH_MACHINE_NAME;
                    taskCategory.CreatedBy = row1.IsTASK_CREATED_BYNull() ? String.Empty : row1.TASK_CREATED_BY;
                    taskCategory.CreatedDate = row1.IsTASK_CREATED_DATENull() ? DateTime.MinValue : row1.TASK_CREATED_DATE;
                    taskCategory.ModifiedBy = row1.IsTASK_MODIFIED_BYNull() ? String.Empty : row1.TASK_MODIFIED_BY;
                    taskCategory.ModifiedDate = row1.IsTASK_MODIFIED_DATENull() ? DateTime.MinValue : row1.TASK_MODIFIED_DATE;
                    taskCategory.BatchModifiedBy = row1.IsMODIFIED_BYNull() ? String.Empty : row1.MODIFIED_BY;
                    taskCategory.TaskStatus = row1.IsTASK_STATUSNull() ? 0 : row1.TASK_STATUS;
                    taskCategory.CmdParameters = row1.IsCMD_PARAMETERSNull() ? null : row1.CMD_PARAMETERS;
                    taskCategory.BatchCmdParameters = row1.IsBATCH_CMD_PARAMETERSNull() ? null : row1.BATCH_CMD_PARAMETERS;
                    taskCategory.ExitCode = row1.IsEXIT_CODENull() ? 0 : row1.EXIT_CODE;
                    taskCategory.BatchId = row1.IsBATCH_IDNull() ? ChoRandom.NextRandom() : row1.BATCH_ID;
                    taskCategory.WorkflowStatus = row1.IsTASK_STATUSNull() ? ChoETLWorkflowStatus.INACTIVE : (ChoETLWorkflowStatus)row1.TASK_STATUS;
                    taskCategory.GroupName = parentTaskGroupCategory.Name;
                    taskCategory.ParentGroupId = row1.IsPARENTNull() ? 0 : row1.PARENT;
                    taskCategory.IsDirty = false;

                    parentTaskGroupCategory.Tasks.Add(taskCategory);
                    _flatCategories.Add(taskCategory);
                }
            }
        }

        public static ChoTasksDataSet.CHO_TASKSDataTable GetData(ChoTaskGroupCategory taskGroupCategory, bool recursive)
        {
            string taskName = null;
            string taskGroupName = null;

            if (taskGroupCategory != null && taskGroupCategory != RootTaskCategory)
            {
                if (!taskGroupCategory.IsTaskGroup)
                {
                    taskName = taskGroupCategory.Name;
                    taskGroupName = taskGroupCategory.GroupName; //.Parent != null ? taskGroupCategory.Parent.Name : taskGroupCategory.Name;
                }
                else
                {
                    taskGroupName = taskGroupCategory.Name;
                }
            }

            using (IChoDataModel dataModel = ChoDataModelFactory.New())
                return dataModel.GetAllUITasks(taskName, taskGroupName);
        }

        public static ChoTasksDataSet.CHO_TASKSDataTable GetData(string taskGroupName, string taskName)
        {
            using (IChoDataModel dataModel = ChoDataModelFactory.New())
                return dataModel.GetAllUITasks(taskName, taskGroupName);
        }

        public List<ChoTaskGroupCategory> FlatCategories
        {
            get { return _flatCategories; }
        }

        public ObservableCollection<ChoTaskGroupCategory> Categories
        {
            get { return _categories; }
            set
            {
                _categories = value;
                RaisePropertyChangedEvent("Categories");
            }
        }

        private void RaisePropertyChangedEvent(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public ChoTaskGroupCategory TryFindTaskGroupById(int id, ChoTaskGroupCategory parent = null)
        {
            if (parent == null)
                parent = RootTaskCategory;

            foreach (ChoTaskGroupCategory category in parent.Tasks)
            {
                if (!category.IsTaskGroup)
                    continue;

                if (category.Id == id)
                    return category;
             
                ChoTaskGroupCategory found = TryFindTaskGroupById(id, category);
                if (found != null)
                    return found;
            }

            return null;
        }

        public ChoTaskGroupCategory TryFindTaskGroupByName(string name, ChoTaskGroupCategory parent = null)
        {
            if (parent == null)
                parent = RootTaskCategory;

            foreach (ChoTaskGroupCategory category in parent.Tasks)
            {
                if (!category.IsTaskGroup)
                    continue;

                if (category.Name == name)
                    return category;

                ChoTaskGroupCategory found = TryFindTaskGroupByName(name, category);
                if (found != null)
                    return found;
            }

            return null;
        }

        private ChoTaskGroupCategory TryFindTaskByName(ChoTaskGroupCategory taskGroupCategory, string taskName)
        {
            foreach (ChoTaskGroupCategory category in taskGroupCategory.Tasks)
            {
                if (category.Name == taskName)
                    return category;
            }

            return null;
        }

        public void Remove(ChoTaskGroupCategory cat)
        {
            if (cat.Parent == null) return;

            lock (_padLock)
            {
                cat.Parent.Tasks.Remove(cat);
                _flatCategories.Remove(cat);
            }
        }
    }
}
